"""

    File: rtmp.py
    Description: 
    
        These factories receive and make connnections to servers that speak 
        Adobe's proprietary RTMP protocol.  Specifically, they implement the
        NetConnection object.

    Author: Kyle Vogt
    Date  : June 5th, 2007
    Copyright (c) 2007, Justin.tv, Inc.
    
"""

from twisted.internet import reactor, protocol, defer
from twisted.python import log
from pms.protocols.rtmp import *
from pms.core.util import ConnectionManager
import traceback
    
class RTMPServer(protocol.ServerFactory, protocol.ClientFactory):
    
    def __init__(self, core, client=False, app=None, tcUrl=None, pageUrl=None, swfUrl=None):
        self.clientMode = client
        if self.clientMode: self.protocol = NetConnectionClient
        else: self.protocol = NetConnection
        self.conn_manager = ConnectionManager(int(core.config['max_conn_per_ip']), int(core.config['max_conn']))
        self.connection_timeout = 15.0
        self.core = core
        self.callbacks = {}
        self.connectDeferred = defer.Deferred()
        self.createStreamDeferred = defer.Deferred()
        self.publishDeferred = defer.Deferred()
        self.playDeferred = defer.Deferred()
        self.showStats()
        self.global_bytes_read = 0
        self.global_bytes_sent = 0

        # Connection parameters
        self.app = app
        self.tcUrl = tcUrl
        self.pageUrl = pageUrl
        self.swfUrl = swfUrl
            
    def showStats(self):
        try:
            bytes = 0
            now = time.time()
            for client in list(self.conn_manager.clients):
                # Check for publisher timeouts
                time_since_packet = (now - client.last_data_time)
                if time_since_packet > self.connection_timeout:
                    for stream in [s for s in client.streams.values() if s.publishing]:
                        try:
                            name = stream.stream.name
                        except:
                            name = 'UNKNOWN'
                        log.msg('Client at %s timed out (%ss) while publishing stream %s' % (client.ip, self.connection_timeout, name))
                        client.shutdown()
                bytes += len(client.transport.dataBuffer)
            if bytes:
                log.msg("%i bytes in outgoing TCP buffers" % bytes)
        except:
            log.msg('error')
            traceback.print_exc()
        reactor.callLater(5.0, self.showStats)
        
    def addCallback(self, trigger, func):
        self.callbacks[trigger].append(func)
        
    def removeCallback(self, trigger, func):
        self.callbacks[trigger].remove(func)
        
    def addClient(self, client):
        "Try to add a new client"
        result = self.conn_manager.add(client)
        self.core.nodes[self.core.name].connections = len(self.conn_manager.clients)
        return result

    def removeClient(self, client):
        "Remove a client from the client list"
        result = self.conn_manager.remove(client)
        self.core.nodes[self.core.name].connections = len(self.conn_manager.clients)

    def get_nc(self):
        if len(self.conn_manager.clients):
            return self.conn_manager.clients[0]
            
    def poll(self):
        for client in self.conn_manager.clients:
            client.poll()

    def invoke(self, command, index, objs=None, streamId=0, channel=None):
        "Make a call on all NetConnections"
        for client in self.conn_manager.clients:
            if client.connected: 
                client.invoke(command, index, objs, streamId, channel)
            else: log.msg("Cannot call %s on %s:%s (not connected)" % (command, client.ip, client.port))
    
